﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Microsoft.Kinect;
using Microsoft.Samples.Kinect.KinectExplorer;
using Microsoft.Samples.Kinect.WpfViewers;
using KinectExplorer;

namespace ZeroTouchUI
{
    /// <summary>
    /// Interaction logic for GR3.xaml
    /// </summary>
    public partial class GR3 : UserControl
    {
        private KinectSensor kinect;
        private bool tracing = false;
        private int countdown = 3;
        private DispatcherTimer countdownTimer;
        private List<KinectSkeleton> skeletons;
        public NewMainWindow mainWindow;
        private bool running = false;
        private GridLength showing = new GridLength(30), hidden = new GridLength(0), resizable = new GridLength(1, GridUnitType.Star), auto = GridLength.Auto;

        public GR3()
        {
            InitializeComponent();

            nameRow.Height = showing;
            handsRow.Height = hidden;
            instructionsRow.Height = hidden;
            viewerRow.Height = hidden;
            saveRow.Height = showing;
        }

        public GR3(Gesture gesture)
        {
            InitializeComponent();

            KinectSkeletonViewerOnColor.skeletonCanvas1.leftGesture = gesture.leftGesture;
            KinectSkeletonViewerOnColor.skeletonCanvas1.rightGesture = gesture.rightGesture;

            nameRow.Height = showing;
            handsRow.Height = showing;
            instructionsRow.Height = auto;
            progressRow.Height = auto;
            viewerRow.Height = resizable;
            saveRow.Height = showing;

            nameTextBox.Text = gesture.Name;
            leftButton.IsChecked = gesture.leftGesture.Count > 0 && gesture.rightGesture.Count == 0;
            rightButton.IsChecked = gesture.leftGesture.Count == 0 && gesture.rightGesture.Count > 0;
            bothButton.IsChecked = gesture.leftGesture.Count > 0 && gesture.rightGesture.Count > 0;

            saveButton.IsEnabled = true;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            mainWindow.recognizer.Recognized += new GestureRecognizer.RecognizedEventHandler(recognizer_Recognized);
            this.cancelButton.Focus();
        }

        public KinectSensor Kinect
        {
            get
            {
                return this.kinect;
            }

            set
            {
                this.kinect = value;
                this.colorViewer.Kinect = this.kinect;
                this.KinectSkeletonViewerOnColor.Kinect = this.kinect;

                if (this.kinect == null)
                {
                    this.KeyUp -= StartRecording;
                    insertKinectSensor.Visibility = System.Windows.Visibility.Visible;
                }
                else
                {
                    this.KeyUp += StartRecording;
                    insertKinectSensor.Visibility = System.Windows.Visibility.Hidden;                    
                }
            }
        }

        private void ShowMoreInfo(object sender, RoutedEventArgs e)
        {
            Hyperlink hyperlink = e.OriginalSource as Hyperlink;
            if (hyperlink != null)
            {
                // Careful - ensure that this NavigateUri comes from a trusted source, as in this sample, before launching a process using it.
                Process.Start(new ProcessStartInfo(hyperlink.NavigateUri.ToString()));
            }

            e.Handled = true;
        }

        public class StopRecordingTimer : System.Timers.Timer
        {
            public List<KinectSkeleton> oldSkeletons;
        }

        private delegate void StopRecordingDelegate();

        private void StopRecording(List<KinectSkeleton> skeletons)
        {
            foreach (KinectSkeleton skeleton in skeletons)
            {
                if (skeleton.leftGesture.Count > 0 ||
                    skeleton.rightGesture.Count > 0)
                {
                    this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() => saveButton.IsEnabled = true));
                }

                skeleton.leftTracing = false;
                skeleton.rightTracing = false;
                skeleton.counter = 0;
            }

            this.tracing = false;
            this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() => progbar.Visibility = System.Windows.Visibility.Hidden));
        }

        public void StartRecording(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == Key.R && viewerRow.Height.Value > 0 && !running && !nameTextBox.IsKeyboardFocused)
            {
                this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() => saveButton.IsEnabled = false));

                skeletons = new List<KinectSkeleton>
                {
                    KinectSkeletonViewerOnColor.skeletonCanvas1,
                    KinectSkeletonViewerOnColor.skeletonCanvas2,
                    KinectSkeletonViewerOnColor.skeletonCanvas3,
                    KinectSkeletonViewerOnColor.skeletonCanvas4,
                    KinectSkeletonViewerOnColor.skeletonCanvas5,
                    KinectSkeletonViewerOnColor.skeletonCanvas6
                };

                if (this.tracing)
                {
                    recordIndicator.Content = "";
                    recordIndicator.Foreground = Brushes.White;
                    recordIndicator.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    recordIndicator.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                    StopRecording(skeletons);
                }
                else
                {
                    foreach (KinectSkeleton skeleton in skeletons)
                    {
                        if (skeleton.rightGesture != null)
                        {
                            skeleton.rightGesture.Clear();
                        }
                        if (skeleton.leftGesture != null)
                        {
                            skeleton.leftGesture.Clear();
                        }
                    }

                    running = true;
                    recordIndicator.Content = "3";
                    countdownTimer = new DispatcherTimer();
                    countdownTimer.Interval = new TimeSpan(0, 0, 1);
                    countdownTimer.Tick += new EventHandler(CountdownTimerStep);
                    countdownTimer.Start();
                }
            }
        }

        private void CountdownTimerStep(object sender, EventArgs e)
        {
            if (countdown > 1)
            {
                countdown--;
                this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() => recordIndicator.Content = countdown.ToString()));
            }
            else
            {
                countdownTimer.Stop();
                countdown = 3;
                recordIndicator.Content = "REC";
                recordIndicator.Foreground = Brushes.Red;
                recordIndicator.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                recordIndicator.VerticalAlignment = System.Windows.VerticalAlignment.Top;
                running = false;
                foreach (KinectSkeleton skeleton in skeletons)
                {
                    skeleton.rightGesture = new List<Point>();
                    skeleton.leftGesture = new List<Point>();
                    skeleton.leftTracing = (bool)(leftButton.IsChecked == true || bothButton.IsChecked == true);
                    skeleton.rightTracing = (bool)(rightButton.IsChecked == true || bothButton.IsChecked == true);
                }
                StopRecordingTimer timer = new StopRecordingTimer { oldSkeletons = skeletons };
                timer.Interval = 3000;
                timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
                timer.Start();

                Duration duration = new Duration(TimeSpan.FromSeconds(3));
                DoubleAnimation doubleanimation = new DoubleAnimation(100.0, duration);
                this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() => 
                    {
                        progbar.Visibility = System.Windows.Visibility.Visible;
                        progbar.BeginAnimation(ProgressBar.ValueProperty, doubleanimation);
                    }));

                this.tracing = true;
            }
        }

        private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            StopRecordingTimer srt = (StopRecordingTimer)sender;
            this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() =>
                {
                    recordIndicator.Content = "";
                    recordIndicator.Foreground = Brushes.White;
                    recordIndicator.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    recordIndicator.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                }));
            StopRecording(srt.oldSkeletons);
            srt.Stop();
        }

        private void recognizer_Recognized(object sender, RecognizedEventArgs e)
        {
            if (this.tracing && 
                e.gesture.Name != ((NewGestureRecorder)this.Parent).gesture.Name && 
                ((NewGestureRecorder)this.Parent).oldGesture != null &&
                e.gesture.Name != ((NewGestureRecorder)this.Parent).oldGesture.Name)
            {
                this.Dispatcher.BeginInvoke(new StopRecordingDelegate(() =>
                {
                    recordIndicator.Content = "";
                    recordIndicator.Foreground = Brushes.White;
                    recordIndicator.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    recordIndicator.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                }));
                StopRecording(skeletons);

                RejectedGesture rg = new RejectedGesture("Gesture " + e.gesture.Name + " was recognized during this recording.\n\n" +
                                                         "Please either erase " + e.gesture.Name + " or record a new gesture.");
                rg.Show();

                foreach (KinectSkeleton skeleton in skeletons)
                {
                    if (skeleton.rightGesture != null)
                    {
                        skeleton.rightGesture.Clear();
                    }
                    if (skeleton.leftGesture != null)
                    {
                        skeleton.leftGesture.Clear();
                    }
                }
            }
        }

        private void saveButton_Click(object sender, RoutedEventArgs e)
        {
            bool isRecorder = this.Parent.GetType().ToString().Contains("GestureRecorder");

            if (this.Kinect != null)
            {
                List<KinectSkeleton> skeletons = new List<KinectSkeleton>
                {
                    KinectSkeletonViewerOnColor.skeletonCanvas1,
                    KinectSkeletonViewerOnColor.skeletonCanvas2,
                    KinectSkeletonViewerOnColor.skeletonCanvas3,
                    KinectSkeletonViewerOnColor.skeletonCanvas4,
                    KinectSkeletonViewerOnColor.skeletonCanvas5,
                    KinectSkeletonViewerOnColor.skeletonCanvas6
                };
                foreach (KinectSkeleton skeleton in skeletons)
                {
                    //Check for ambiguous cases before saving
                    Gesture recognized = Dollar.Recognize(skeleton.leftGesture, skeleton.rightGesture, 0.55);
                    if (recognized != null && 
                        recognized.Name != ((NewGestureRecorder)this.Parent).gesture.Name &&
                        ((NewGestureRecorder)this.Parent).oldGesture != null &&
                        recognized.Name != ((NewGestureRecorder)this.Parent).oldGesture.Name)
                    {
                        RejectedGesture rg = new RejectedGesture("The gesture you have recorded has a high risk of being confused with " +
                                                                  recognized.Name + ".\n\nPlease either erase " + recognized.Name +
                                                                  " or record a new gesture.");
                        rg.ShowDialog();
                        return;
                    }

                    if ((skeleton.leftGesture != null && skeleton.leftGesture.Count > 0) ||
                        (skeleton.rightGesture != null && skeleton.rightGesture.Count > 0))
                    {
                        skeleton.leftGesture = Dollar.Resample(skeleton.leftGesture);
                        skeleton.rightGesture = Dollar.Resample(skeleton.rightGesture);
                        if (skeleton.leftGesture != null && skeleton.leftGesture.Count > 0)
                        {
                            skeleton.leftGesture = Dollar.ScaleWithSharpen(skeleton.leftGesture);
                            skeleton.leftGesture = Dollar.TranslateToOrigin(skeleton.leftGesture);
                        }
                        if (skeleton.rightGesture != null && skeleton.rightGesture.Count > 0)
                        {
                            skeleton.rightGesture = Dollar.ScaleWithSharpen(skeleton.rightGesture);
                            skeleton.rightGesture = Dollar.TranslateToOrigin(skeleton.rightGesture);
                        }
                    }

                    if (skeleton.leftGesture != null && skeleton.leftGesture.Count > 0 &&
                        skeleton.rightGesture != null && skeleton.rightGesture.Count > 0)
                    {
                        if (isRecorder)
                        {
                            ((NewGestureRecorder)this.Parent).gesture.leftGesture = skeleton.leftGesture;
                            ((NewGestureRecorder)this.Parent).gesture.rightGesture = skeleton.rightGesture;
                        }
                    }
                    else
                    {
                        if (skeleton.leftGesture != null && skeleton.leftGesture.Count > 0)
                        {
                            if (isRecorder)
                            {
                                ((NewGestureRecorder)this.Parent).gesture.leftGesture = skeleton.leftGesture;
                            }
                        }
                        if (skeleton.rightGesture != null && skeleton.rightGesture.Count > 0)
                        {
                            if (isRecorder)
                            {
                                ((NewGestureRecorder)this.Parent).gesture.rightGesture = skeleton.rightGesture;
                            }
                        }
                    }
                }
            }

            if (isRecorder)
            {
                ((NewGestureRecorder)this.Parent).gesture.Name = nameTextBox.Text;
                ((NewGestureRecorder)this.Parent).OnSaved(new EventArgs());
            }
        }

        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            ((NewGestureRecorder)this.Parent).Close();
        }

        private void UpdateName()
        {
            if (!String.IsNullOrWhiteSpace(nameTextBox.Text))
            {
                handsRow.Height = showing;
                ((NewGestureRecorder)this.Parent).gesture.Name = nameTextBox.Text;
                bool hasCheck = (leftButton.IsChecked == true || rightButton.IsChecked == true || bothButton.IsChecked == true);

                instructionsRow.Height = hasCheck ? auto : hidden;
                progressRow.Height = hasCheck ? auto : hidden;
                viewerRow.Height = hasCheck ? resizable : hidden;
                saveButton.IsEnabled = hasCheck && (((NewGestureRecorder)this.Parent).gesture.leftGesture.Count > 0 ||
                                                    ((NewGestureRecorder)this.Parent).gesture.rightGesture.Count > 0);
            }
            else
            {
                saveButton.IsEnabled = false;
            }
        }

        private void nameTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                ((TextBox)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            }
        }

        private void nameTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
        
        }

        private void nameTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            UpdateName();
        }

        private void handButton_Checked(object sender, RoutedEventArgs e)
        {
            instructionsRow.Height = auto;
            progressRow.Height = auto;
            viewerRow.Height = resizable;
        }
    }
}
